/**
 * 
 */
package org.bspv.pharma.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.bspv.pharma.dao.DAO;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 *
 * @author guillaume
 *
 * @param <E>
 * @param <ID>
 */
public abstract class AbstractHibernateDAO<E, ID extends Serializable> extends HibernateDaoSupport implements
        DAO<E, ID> {

    /**
     * 
     */
    public AbstractHibernateDAO() {
        super();
    }

    /**
     * 
     */
    public void delete(E e) {
        getHibernateTemplate().delete(e);
    }

    /**
     * 
     */
    public E findById(ID id) {
        return (E) getHibernateTemplate().get(getManagedType(), id);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public ID save(E e) {
        ID id = (ID) getHibernateTemplate().save(e);
        return id;
    }
    
    /**
     * 
     */
    public void saveOrUpdate(E e) {
        getHibernateTemplate().saveOrUpdate(e);
    }

    /**
     * 
     */
    public E refresh(E e) {
        getHibernateTemplate().refresh(e);
        return e;
    }

    /**
     * 
     */
    public List<E> save(List<E> elements) {
        getHibernateTemplate().saveOrUpdateAll(elements);
        return elements;
    }

    /**
     * 
     */
    public void delete(List<E> elements) {
        getHibernateTemplate().deleteAll(elements);
    }

    /**
     * 
     */
    public List<E> refresh(List<E> elements) {
        for (E e : elements)
            getHibernateTemplate().refresh(e);
        return elements;
    }

    /**
     * 
     */
    public List<E> findAll() {
        return getHibernateTemplate().loadAll(getManagedType());
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findAllOrdered(String order, boolean ascendant) {
        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        crit.addOrder(ascendant ? Order.asc(order) : Order.desc(order));
        return getHibernateTemplate().findByCriteria(crit);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findBy(String attr, Object value) {
        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        crit.add(Restrictions.eq(attr, value));
        return getHibernateTemplate().findByCriteria(crit);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findByOrdered(String attr, Object value, String order, boolean ascendant) {
        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        crit.add(Restrictions.eq(attr, value));
        crit.addOrder(ascendant ? Order.asc(order) : Order.desc(order));
        return getHibernateTemplate().findByCriteria(crit);
    }

    /**
     * 
     */
    public List<E> findByExample(E example) {
        return findByExample(example, null, false);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findByExample(E example, List<String> propertiesToIgnore, boolean enableLike) {
        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        Example ex = Example.create(example).excludeZeroes();
        if (!enableLike)
            ex.enableLike();

        if (propertiesToIgnore != null) {
            for (int i = 0; i < propertiesToIgnore.size(); i++) {
                ex.excludeProperty(propertiesToIgnore.get(i));
            }
        }
        return getHibernateTemplate().findByCriteria(crit.add(ex));
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findBy(String[] attr, Object[] value) {
        if (attr.length != value.length)
            throw new IllegalArgumentException("Criteria lists must have the same dimension");

        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        for (int i = 0; i < attr.length; i++)
            crit.add(Restrictions.eq(attr[i], value[i]));

        return getHibernateTemplate().findByCriteria(crit);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findByOrdered(String[] attr, Object[] value, String order, boolean ascendant) {
        if (attr.length != value.length)
            throw new IllegalArgumentException("Criteria lists must have the same dimension");

        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        for (int i = 0; i < attr.length; i++)
            crit.add(Restrictions.eq(attr[i], value[i]));

        crit.addOrder(ascendant ? Order.asc(order) : Order.desc(order));
        return getHibernateTemplate().findByCriteria(crit);
    }

    /**
     * 
     * @return
     */
    @SuppressWarnings("unchecked")
    protected Class<E> getManagedType() {
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        Class<E> c = (Class<E>) pt.getActualTypeArguments()[0];
        return c;
    }

    /**
     * 
     */
    public E findUniqueResultBy(String attr, Object value) {
        List<E> c = this.findBy(attr, value);

        if (c.size() > 0) {
            return c.iterator().next();
        } else {
            return null;
        }
    }

    /**
     * 
     */
    public E findUniqueResultBy(String[] attr, Object[] value) {
        List<E> c = this.findBy(attr, value);

        if (c.size() > 0) {
            return c.iterator().next();
        } else {
            return null;
        }
    }

    /**
     * 
     */
    public void evict(List<E> elements) {
        for (E e : elements)
            evict(e);
    }

    /**
     * 
     */
    public void evict(E e) {
        getHibernateTemplate().evict(e);

    }

    /**
     * 
     */
    public void evictAll() {
        getHibernateTemplate().clear();
    }

    /**
     * 
     */
    public void flush() {
        getHibernateTemplate().flush();
    }

    /**
     * 
     */
    public int getElementCount() {
        Session session = SessionFactoryUtils.getSession(getHibernateTemplate().getSessionFactory(), true); 
        Query query = session.createQuery("select count(*) from " + getManagedType().getName());
        return ((Long) query.uniqueResult()).intValue();
    }

    /**
     * 
     */
    public E merge(E e) {
        e = (E) getHibernateTemplate().merge(e);
        return e;
    }

    /**
     * 
     */
    public void merge(List<E> elements) {
        for (E e : elements) {
            merge(e);
        }
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findBy(String attr, Object value, int firstResult, int nbMax) {
        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        crit.add(Restrictions.eq(attr, value));
        return getHibernateTemplate().findByCriteria(crit, firstResult, nbMax);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findByOrdered(String attr, Object value, String order, boolean ascendant, int firstResult, int nbMax) {
        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        crit.add(Restrictions.eq(attr, value));
        crit.addOrder(ascendant ? Order.asc(order) : Order.desc(order));
        return getHibernateTemplate().findByCriteria(crit, firstResult, nbMax);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findBy(String[] attr, Object[] value, int firstResult, int nbMax) {
        if (attr.length != value.length)
            throw new IllegalArgumentException("Criteria lists must have the same dimension");

        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        for (int i = 0; i < attr.length; i++)
            crit.add(Restrictions.eq(attr[i], value[i]));

        return getHibernateTemplate().findByCriteria(crit, firstResult, nbMax);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findByOrdered(String[] attr, Object[] value, String order, boolean ascendant, int firstResult,
            int nbMax) {
        if (attr.length != value.length)
            throw new IllegalArgumentException("Criteria lists must have the same dimension");

        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        for (int i = 0; i < attr.length; i++)
            crit.add(Restrictions.eq(attr[i], value[i]));

        crit.addOrder(ascendant ? Order.asc(order) : Order.desc(order));
        return getHibernateTemplate().findByCriteria(crit, firstResult, nbMax);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findBy(String[] attr, Object[] value, String[] nullProperties, String[] notNullProperties) {
        if (attr.length != value.length)
            throw new IllegalArgumentException("Criteria lists must have the same dimension");

        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        for (int i = 0; i < attr.length; i++)
            crit.add(Restrictions.eq(attr[i], value[i]));
        for (int i = 0; i < nullProperties.length; i++)
            crit.add(Restrictions.isNull(nullProperties[i]));
        for (int i = 0; i < notNullProperties.length; i++)
            crit.add(Restrictions.isNotNull(notNullProperties[i]));
        return getHibernateTemplate().findByCriteria(crit);
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    public List<E> findByOrdered(String[] attr, Object[] value, String[] nullProperties, String[] notNullProperties,
            String order, boolean ascendant) {
        if (attr.length != value.length)
            throw new IllegalArgumentException("Criteria lists must have the same dimension");

        DetachedCriteria crit = DetachedCriteria.forClass(getManagedType());
        for (int i = 0; i < attr.length; i++)
            crit.add(Restrictions.eq(attr[i], value[i]));
        for (int i = 0; i < nullProperties.length; i++)
            crit.add(Restrictions.isNull(nullProperties[i]));
        for (int i = 0; i < notNullProperties.length; i++)
            crit.add(Restrictions.isNotNull(notNullProperties[i]));
        crit.addOrder(ascendant ? Order.asc(order) : Order.desc(order));
        return getHibernateTemplate().findByCriteria(crit);
    }
}
